package com.igeek.boot;

import com.igeek.boot.config.MyConfig1;
import com.igeek.boot.config.MyConfig2;
import com.igeek.boot.config.MyConfig3;
import com.igeek.boot.config.MyConfig4;
import com.igeek.boot.entity.Address;
import com.igeek.boot.entity.Car;
import com.igeek.boot.entity.Person;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

import java.util.Arrays;

/**
 * @Author fengqq
 * @Description 主启动类
 * @Date 2024/8/13  10:45
 *
 * 注解：@SpringBootApplication 复合注解  作用：标记主启动类
 *
 * 复合注解  取代了以下三个注解：
 * 1.@SpringBootConfiguration 声明当前是配置类
 *          作用等价于@Configuration (又等价于@Componment)
 * 2.@EnableAutoConfiguration 开启自动配置
 *      2.1 @AutoConfigurationPackage  自动包规则配置
 *              --> @Import(AutoConfigurationPackages.Registrar.class)
 *                  1.开启自动包规则配置，原理：将当前主启动类所在的包下，所有标注了类似于@Component注解的组件实例，都注册到IOC容器中
 *                  2.如果想指定 扫描包规则的 则可以@SpringBootApplication(scanBasePackages = "com.igeek.boot")
 *      2.2 @Import(AutoConfigurationImportSelector.class)
 *                  初始默认加载所有的xxxAutoConfiguration
 *                  1.加载 org.springframework.boot.autoconfigure.AutoConfiguration.imports 144个场景启动器
 *                  2.若在pom.xml中引入xxx-starter场景依赖，则激活相关配置类xxxAutoConfiguration
 * 3.@ComponentScan 配置扫描包
 *
 * 自动配置流程：
 *      注解@SpringBootApplication
 *      --> 注解@EnableAutoConfiguration
 *      -->@Import(AutoConfigurationImportSelector.class)
 *      --> org.springframework.boot.autoconfigure.AutoConfiguration.imports文件 --》144个场景启动器
 *      --> 在pom.xml中引入xxx-starter场景依赖，则激活相关配置类xxxAutoConfiguration
 *
 * 定制配置：
 *      以DispatcherServletAutoConfiguration为例：
 *      1.参考 @ConditionalOnxxx 生效配置的条件
 *      2.参考 @EnableConfigurationProperties(WebMvcProperties.class) 开启配置绑定，在application.properties文件中 通过前缀spring.mvc来干涉容器中的bean配置信息
 *      3.参考 @Bean 将当前方法的返回值注册到ioc容器中以方法名作为beanName --> @Configuration+ @Bean替换底层组件
 *
 * 找两个配置类 DispatcherServletAutoConfiguration ,WebMvcAutoConfiguration等，
 * 1.什么时候会生效配置类？
 * 2.开启配置绑定了什么？使用什么前缀来操作的？
 * 3.@Bean注入了哪些组件？能否自己替换？
 *
 */
@SpringBootApplication
public class HelloSpringBootApplication {
    public static void main(String[] args) {
        //just run 得到IOC容器(ConcurrentHashMap<String,BeanDefinition> key:唯一标识 value:bean的定义)
        ConfigurableApplicationContext context = SpringApplication.run(HelloSpringBootApplication.class, args);

        //得到IOC容器中 所有的bean定义信息(<bean id="" class=""> 类型，作用域,init初始化方法,destory销毁方法)
//        String[] beanDefinitionNames = context.getBeanDefinitionNames();
//        Stream.of(beanDefinitionNames).forEach(System.out::println);

        System.out.println("===========MyConfig1=================");
        MyConfig1 myConfig1 = context.getBean("myConfig1", MyConfig1.class);
        System.out.println("myConfig1 = " + myConfig1);//cglib动态代理对象 com.igeek.boot.config.MyConfig1$$EnhancerBySpringCGLIB$$ff1dfd69@4a2929a4

        Person person = context.getBean("person", Person.class);
        System.out.println("person = " + person);
        Person person1 = context.getBean("person", Person.class);
        System.out.println("person1 = " + person1);
        System.out.println(person == person1);//true

        Car car = context.getBean("car", Car.class);
        System.out.println("car = " + car);

        System.out.println("===========MyConfig2=================");
        MyConfig2 myConfig2 = context.getBean("myConfig2", MyConfig2.class);
        System.out.println("myConfig2 = " + myConfig2);

        //根据类型 获取 所有的bean名称
        String[] beanNames = context.getBeanNamesForType(Person.class);
        System.out.println(Arrays.toString(beanNames));

        System.out.println("===========MyConfig3=================");
        MyConfig3 myConfig3 = context.getBean("myConfig3", MyConfig3.class);
        System.out.println("myConfig3 = " + myConfig3);

        String[] beanNamesForCar = context.getBeanNamesForType(Car.class);
        System.out.println(Arrays.toString(beanNamesForCar));//[car, c1]

        System.out.println("===========MyConfig4=================");
        MyConfig4 MyConfig4 = context.getBean("myConfig4", MyConfig4.class);
        System.out.println("MyConfig4 = " + MyConfig4);
        String[] beanNamesForAddress = context.getBeanNamesForType(Address.class);
        System.out.println(Arrays.toString(beanNamesForAddress));//addr-com.igeek.boot.entity.Address
        Address address = context.getBean("addr-com.igeek.boot.entity.Address", Address.class);
        System.out.println("address = " + address);


    }
}
